
from whiff.middleware import misc
from whiff import resolver
from whiff import whiffenv

BINARY_OPERATIONS = ["/", "+", "-", "x"]
UNARY_OPERATIONS = ["neg", "push", "pop"]

class calcCallBack(misc.utility):
    def __call__(self, env, start_response):
        env = resolver.process_cgi(env, parse_cgi=True)
        mode = whiffenv.cgiGet(env, "mode", "")
        display = whiffenv.cgiGet(env, "display", "0")
        value = whiffenv.cgiGet(env, "value", "0")
        stack = whiffenv.cgiGet(env, "stack", "")
        operation = whiffenv.cgiGet(env, "operation", "")
        proxy = Proxy(display, value, stack, operation)
        # validate the display value
        try:
            displayValue = float(display)
        except ValueError:
            displayValid = False
        else:
            displayValid = True
        if mode=="clear":
            proxy.display = "0"
            proxy.operation =""
            proxy.message = "cleared"
        elif not displayValid:
            proxy.message = "invalid numeric data"
            proxy.value = "0"
        elif mode=="ENTER":
            self.doOperation(proxy, value, operation, display)
            proxy.operation = ""
        elif mode in BINARY_OPERATIONS:
            self.doOperation(proxy, value, operation, display)
            proxy.operation = mode
            proxy.message = "%s %s" % (proxy.display, mode)
            proxy.display = 0
        elif mode in UNARY_OPERATIONS:
            self.doOperation(proxy, value, operation, display)
            self.doUnary(proxy, mode)
        else:
            proxy.message = "unknown mode ("+mode+")"
        response = proxy.javascript()
        #print "response\n", response
        start_response("200 OK", [('Content-Type', 'application/javascript')])
        return [response]

    def doUnary(self, proxy, mode):
        display = float(proxy.display)
        if mode=="neg":
            proxy.display = proxy.value = -display
        elif mode=="push":
            proxy.stack = proxy.stack+" "+str(proxy.display)
            proxy.message = "pushed "+proxy.display
        elif mode=="pop":
            sstack = proxy.stack.split()
            if not sstack:
                proxy.message = "stack underflow"
            else:
                popped = sstack[0]
                proxy.stack = " ".join(sstack[1:])
                proxy.display = popped
                proxy.message = "popped "+str(popped)
        else:
            proxy.message = "unknown unary mode ("+mode+")"

    def doOperation(self, proxy, value, operation, display):
        v = float(value)
        d = float(display)
        if operation=="/":
            if d==0:
                proxy.message = "divide by zero not defined"
                proxy.display = 0
            else:
                proxy.display = proxy.value = r = v/d
                proxy.message = "%s / %s = %s" % (v,d,r)
        elif operation=="x":
            proxy.display = proxy.value = r = v*d
            proxy.message = "%s x %s = %s" % (v,d,r)
        elif operation=="+":
            proxy.display = proxy.value = r = v+d
            proxy.message = "%s + %s = %s" % (v,d,r)
        elif operation=="-":
            proxy.display = proxy.value = r = v-d
            proxy.message = "%s - %s = %s" % (v,d,r)
        elif operation=="":
            # no operation, copy display to value
            proxy.value = proxy.display
        else:
            proxy.message = "unknown operation ("+operation+")"
            proxy.display = 0
        return proxy
    
def setMessage(message):
    "create javascript to display a message"
    return """
    (function () {
    var elt = document.getElementById("message");
    elt.innerHTML = "%s";
    } ) ();
    """ % (message,)
          
def setValue(identity, value):
    "create a javascript fragment to set an input element value"
    return """
    // create and call anonymous function
    (function () {
    var elt = document.getElementById("%s");
    elt.value = "%s";
    } ) ();
    """ % (identity, value)

class Proxy:
    def __init__(self, display, value, stack, operation):
        self.display = self.display0 = display
        self.value = self.value0 = value
        self.stack = self.stack0 = stack
        self.operation = self.operation0 = operation
        self.message = None

    def javascript(self):
        result = ""
        if self.message:
            result += setMessage(self.message)
        if self.display0!=self.display:
            result += setValue("display", self.display)
        if self.value0!=self.value:
            result += setValue("value", self.value)
        if self.stack0!=self.stack:
            result += setValue("stack", self.stack)
        if self.operation0!=self.operation:
            result += setValue("operation", self.operation)
        return result

__middleware__ = calcCallBack
